package com.ruubypay.biz.mvc.workflow.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.ruubypay.biz.mvc.workflow.model.request.FlowModel;
import com.ruubypay.biz.mvc.workflow.service.ModelService;
import com.ruubypay.biz.utils.io.resource.ResourceConverter;
import com.ruubypay.web.common.page.DataGrid;
import com.ruubypay.web.common.utils.StringUtils;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ModelQuery;
import org.bouncycastle.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.*;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * 模型管理服务层
 * @author chenhaiyang
 */
@Service("modelService")
public class ModelServiceImpl implements ModelService{

    /**
     *  流程的命名空间，禁止修改
     */
    private static final String KEY_NAMESPACE="namespace";
    private static final String VALUE_NAMESPACE ="http://b3mn.org/stencilset/bpmn2.0#";
    /**
     * 流程唯一标识
     */
    private static final String KEY_PROCESS_ID="process_id";
    /**
     * 流程作者
     */
    private static final String KEY_PROCESS_AUTHOR="process_author";
    /**
     * 流程名称
     */
    private static final String KEY_PROCESS_NAME="name";
    /**
     * 编辑器id
     */
    private static final String KEY_EDITOR_ID="id";
    private static final String VALUE_EDITOR_ID="canvs";
    /**
     * 编辑器资源id
     */
    private static final String KEY_RESOURCES_ID="resourceId";
    private static final String VALUE_RESOURCES_ID="canvs";
    /**
     * 流程节点作者
     */
    private static final String NODE_AUTHOR ="author";
    /**
     * 模型名称
     */
    private static final String MODEL_NAME="name";
    /**
     *  模型版本
     */
    private static final String MODEL_VERSION="revision";
    /**
     * 模型描述
     */
    private static final String MODEL_DESCRIPTION="description";


    private static Logger logger = LoggerFactory.getLogger(ModelServiceImpl.class);

    @Resource
    private RepositoryService repositoryService;
    /**
     * 获取模型列表
     * @param paramMap 参数
     * @param pageNumber 页码
     * @param pageSize 页数
     * @return 数据集
     */
    @Override
    public DataGrid listDataModels(Map<String, Object> paramMap, Integer pageNumber, Integer pageSize) {

        ModelQuery modelQuery = repositoryService.
                createModelQuery().
                latestVersion().orderByLastUpdateTime().desc();

        String category = String.valueOf(paramMap.get("category"));
        String modelName = String.valueOf(paramMap.get("modelName"));
        if (StringUtils.isNotBlank(category)) {
            modelQuery.modelCategory(category);
        }
        if(StringUtils.isNotBlank(modelName)){
            modelQuery.modelNameLike(modelName);
        }

        List<Model> modelList = modelQuery.listPage((pageNumber-1)*pageSize,pageSize);
        return new DataGrid(modelQuery.count(), modelList);
    }

    /**
     *  新增模型
     * @param flowModel 模型
     * @return 返回结果
     */
    @Override
    public String saveModel(FlowModel flowModel) throws UnsupportedEncodingException {

        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode editorNode = objectMapper.createObjectNode();
        editorNode.put(KEY_EDITOR_ID, VALUE_EDITOR_ID);
        editorNode.put(KEY_RESOURCES_ID, VALUE_RESOURCES_ID);

        ObjectNode stencilSetNode = objectMapper.createObjectNode();
        stencilSetNode.put(KEY_NAMESPACE, VALUE_NAMESPACE);
        stencilSetNode.put(NODE_AUTHOR, "");
        editorNode.set("stencilset", stencilSetNode);

        ObjectNode propertiesNode = objectMapper.createObjectNode();
        propertiesNode.put(KEY_PROCESS_ID,flowModel.getProcessId());
        propertiesNode.put(KEY_PROCESS_AUTHOR,flowModel.getProcessUser());
        propertiesNode.put(KEY_PROCESS_NAME,flowModel.getName());
        editorNode.set("properties", propertiesNode);

        ObjectNode modelObjectNode = objectMapper.createObjectNode();
        modelObjectNode.put(MODEL_NAME, flowModel.getModelName());
        modelObjectNode.put(MODEL_VERSION, 1);
        modelObjectNode.put(MODEL_DESCRIPTION, flowModel.getDescription());

        Model modelData = repositoryService.newModel();
        modelData.setCategory(flowModel.getCategory());
        modelData.setKey(flowModel.getProcessId());
        modelData.setMetaInfo(modelObjectNode.toString());
        modelData.setName(flowModel.getModelName());
        modelData.setTenantId("");

        //保存模型,存储数据到表：act_re_model 流程设计模型部署表
        repositoryService.saveModel(modelData);
        //保存资源,存储数据到表：act_ge_bytearray 二进制数据表
        repositoryService.addModelEditorSource(modelData.getId(),
                editorNode.toString().getBytes("utf-8"));

        return modelData.getId();
    }

    /**
     * 删除模型
     */
    @Override
    public int deleteModelService(String idArray) {
        Arrays.asList(Strings.split(idArray,',')).forEach(
            id ->  repositoryService.deleteModel(id)
        );
        return 1;
    }

    /**
     * 部署模型
     * @param flowModel 模型
     * @return 部署结果
     */
    @Override
    public String deploymentModel(FlowModel flowModel) throws IOException {
        Model modelData = repositoryService.getModel(flowModel.getId());
        ObjectNode modelNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));

        BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
        byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);

        String processName = modelData.getName() + ".bpmn20.xml";
        //部署名称
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment().name(modelData.getName());
        //完成部署
        Deployment deployment = deploymentBuilder.addString(processName, new String(bpmnBytes,"utf-8")).deploy();
        //返回部署ID
        return deployment.getId();
    }

    /**
     * 查看xml模型图
     * @param modelId 模型id
     * @return 返回xml
     */
    @Override
    public String viewXmlFromModelId(String modelId) throws IOException {

        Model modelData = repositoryService.getModel(modelId);

        ObjectNode modelNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
        BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(modelNode);
        byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(bpmnModel);

        ByteArrayInputStream in = new ByteArrayInputStream(bpmnBytes);
        return ResourceConverter.converterInputStreamAsString(in);
    }

    /**
     * 判断能否正常导出xml文件
     * @param flowModel 流程对象
     * @return 返回结果
     */
    @Override
    public String isCanexportXmlFromModelId(FlowModel flowModel) {

        String isOk="true";
        try {
            Model modelData = repositoryService.getModel(flowModel.getId());
            ObjectNode modelNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
            BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(modelNode);
            new BpmnXMLConverter().convertToXML(bpmnModel);
        } catch (IOException e) {
            logger.error(e.getMessage(),e);
            isOk="false";
        }
        return isOk;
    }

    /**
     * 导出模型
     * @param modelId 模型id
     * @return 返回模型Object数组 [0] 模型byte [1]文件名称
     */
    @Override
    public Object[] exportXmlFromModelId(String modelId) {

        Model modelData = repositoryService.getModel(modelId);
        Object[] results = new Object[2];
        byte[] bpmnBytes= new byte[0];
        String filename="";
        try {
            ObjectNode modelNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
            BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(modelNode);
            bpmnBytes = new BpmnXMLConverter().convertToXML(bpmnModel);
            filename = bpmnModel.getMainProcess().getId() + ".bpmn20.xml";

        } catch (IOException e) {
            logger.error(e.getMessage(),e);
        }
        results[0]=bpmnBytes;
        results[1]=filename;

        return results;

    }
}
